﻿# Мобильное приложение для Windows Phone
----------------------------------------
В данной работе мы напишем мобильное приложение, которое обеспечит полноценное взаимодействие с Tessel через мобильную службу Azure.

Вместо создания приложения с нуля мы используем готовое, предоставляемое как образец на портале управления Azure.

Для выполнения работы вам понадобится Visual Studio 2013 Update 4 с эмуляторами Windows Phone 8.1 или реальное устройство. Работа эмулятора Windows Phone 8.1 возможна только в Windows 8.1.

## Создание пустого приложения

1. Запустите Visual Studio 2013 Update 4 и выберите из меню **File** - **New** - **Project**.

2. В появившемся окне выберите тип проекта **Windows Phone Apps** - **Blank App**, задайте имя и размещение проекта:
<p align="center"><img src="images\wp_project_type.jpg" width=90%/></p>

3. Выберите в меню **Build** - **Build Solution** и убедитесь, что приложение было собрано. Запустите его на эмуляторе, выбрав нужный эмулятор из выпадающего списка и повторно кликнув на нем:
<p align="center"><img src="images\emulator.jpg" width=90%/></p>
> Запуск эмулятора и развертывание приложения могут занять длительное время.

4. Не завершайте эмулятор, позднее мы будем использовать его повторно. Вернитесь к окну Visual Studio и выберите **Debug** - **Stop Debugging**.

## Подключение приложения к мобильной службе Azure

1. Кликните на названии проекта (а не Solution) правой кнопкой и выберите команду **Manage NuGet Packages**. В строке поиска справа вверху появившегося окна наберите **MobileServices** и нажмите **Enter**. По завершению поиска в списке пакетов найдите **Windows Azure Mobile Services** и нажмите кнопку **Install**. Вам будет предложено разрешить зависимости и принять условия лицензионных соглашений - согласитесь. По завершению процесса закройте окно.

2. Откройте файл **App.xaml.cs** и в конец списка директив using добавьте:
```
using Microsoft.WindowsAzure.MobileServices;
```

3. Проделайте аналогично в файле **MainPage.xaml.cs** добавьте:
```
using Microsoft.WindowsAzure.MobileServices;
using Windows.UI.Popups;
using Newtonsoft.Json.Linq;
```

4. В определение класса **App** (сразу после открывающей фигурной скобки) добавьте:
```
public static MobileServiceClient MobileService = new MobileServiceClient(
    "https://<имя вашей мобильной службы>.azure-mobile.net/",
    "<ключ приложения>"
);
```
Замените ```<имя вашей мобильной службы>``` на соответствующее вашему случаю.

5. Аналогично в файле **MainPage.xaml.cs** добавьте в определение класса MainPage строку:
```
private string TesselId = "<ваш Tessel ID>";
```
и замените ```<ваш Tessel ID>``` на уникальный идентификатор платы Tesselю

6. Войдите на портал управления Azure и перейдите к созданной вами ранее мобильной службе.

7. Перейдите на стартовую страницу мобильной службы, обозначенную значком <img src="images\cloud.jpg"/>.

8. Внизу страницы выберите **Управление ключами**.

9. Скопируйте ключ приложения и вставьте его вместо ```<ключ приложения>```.

10. Найдите код
```
namespace <имя проекта>
{
```
и после открывающей фигурной скобки вставьте код, отвечающий элементу таблицы (обратите внимание, что элементы **t** и **rh** имеют тип, отличающийся от указанного в таблице, но это не вызовет конфликта):
```
public class dev_data
{
    public string id { get; set; }
    public string deviceid { get; set; }
    public string userid { get; set; }
    public string t { get; set; }
    public string rh { get; set; }
    public int led0 { get; set; }
    public int led1 { get; set; }
}
```

11. Перейдите к редактированию дизайна, выбрав **MainPage.xaml**.
> Вы можете переключаться между кодом и дизайном, нажимая **F7** и **Shift + F7**.

12. Найдите элемент
```
<Grid>
</Grid>
```
В его тело (между элементов) вставьте код, приведенный ниже:
```
<TextBlock FontSize="36" Margin="5,0,0,0" Text="Tessel Azure Test"/>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="40,70,0,0" TextWrapping="Wrap" Text="t (C):" Height="25" Width="120" FontSize="20"/>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="40,100,0,0" TextWrapping="Wrap" Text="RH (%):" Height="25" Width="120" FontSize="20"/>
<TextBlock Name="t_value" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="200,70,0,0" TextWrapping="Wrap" Text="NA" Height="25" Width="120" FontSize="20"/>
<TextBlock Name="rh_value" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="200,100,0,0" TextWrapping="Wrap" Text="NA" Height="25" Width="120" FontSize="20"/>
<ToggleSwitch Header="led 0" Name="led0_value" HorizontalAlignment="Left" Margin="40,149,0,0" VerticalAlignment="Top" Width="180" FontSize="20" Height="70"/>
<ToggleSwitch Header="led 1" Name="led1_value" HorizontalAlignment="Left" Margin="40,221,0,0" VerticalAlignment="Top" Width="180" FontSize="20" Height="70"/>
<Button Name="Refresh" Content="Refresh" HorizontalAlignment="Left" Margin="40,310,0,0" VerticalAlignment="Top" Width="208"/>
```
В окне дизайна появится соответствующий дизайн (см. рисунок ниже). Ознакомьтесь с ним. Обратите внимание: для упрощения мы не использовали никаких методов выравнивания и адаптации интерфейса к размеру экрана.
<p align="center"><img src="images\design.jpg"/></p>

13. В окне дизайна дважды кликните на кнопке с надписью **Refresh**. Будет создан обработчик события **OnClick** и вы попадете в соответствующий учаток кода. Здесь нам необходимо произвести обмен данными с таблицей мобильной службы: сначала прочитать значения температуры и влажности, затем записать в таблицу значения для светодиодов (допустимые значения 0 или 1). Эти действия мы будем выполнять с помощью библиотеки клиента мобильных служб.
> Код на Tessel автоматически производит аналогичный обмен (но в противоположном направлении) каждые 30 секунд (см. код api-client.js).

14. Вставьте в обработчик события следующий код:
```
            MobileServiceCollection<dev_data, dev_data> items = await App.MobileService.GetTable<dev_data>()
                .Where(dev_item => dev_item.deviceid == TesselId)
                .ToCollectionAsync();
            switch (items.Count)
            {
                case 0:
                    await new MessageDialog("No data for the device ID", "Warning").ShowAsync();
                    break;
                case 1:
                    // data from cloud to app
                    t_value.Text = items[0].t;
                    rh_value.Text = items[0].rh;
                    // data from app to cloud - update only leds state
                    JObject jo = new JObject();
                    jo.Add("id", items[0].id);
                    jo.Add("led0", led0_value.IsOn ? 0 : 1);
                    jo.Add("led1", led1_value.IsOn ? 0 : 1);
                    await App.MobileService.GetTable<dev_data>().UpdateAsync(jo);
                    break;
                default:
                    await new MessageDialog("Multiple devices not implemented yet", "Warning").ShowAsync();
                    break;
            }
```

15. Измените заголовок метода
```
private void Refresh_Click(object sender, RoutedEventArgs e)
```
на
```
private async void Refresh_Click(object sender, RoutedEventArgs e)
```

16. Выберите **Build** - **Build Solution** и запустите проект на эмуляторе.

17. Убедитесь, что Tessel все еще работает и осуществляет обмен данными с API.

18. На эмуляторе Windows Phone измените значения одного из элементов **led0** или **led1** и убедитесь, что при нажатии на кнопку **Refresh** происходит обновление данных о температуре и влажности, а Tessel зажигает светодиоды в соответствии с состояними элементов **led0**, **led1**.
> Учтите, что поскольку Tessel обменивается с облаком данными раз в 30 секунд, задержка в передаче данных в обе стороны может составлять до 30 секунд.

19. Воздействуйте на датчик климатического модуля Tessel и наблюдайте изменения в интерфейсе мобильного приложения, нажимая кнопку **Refresh**. Наблюдайте также изменения в консоли Tessel.
> Во избежание повреждения датчика влажности не проливайте на него жидкости и не трогайте его пальцами.

<p align="center"><img src="images\app_on_emulator.jpg"/></p>

## Улучшение созданного приложения *(для самостоятельного выполнения)*

Рассмотренное приложение является одной из простейших реализаций обмена данными с IoT устройством. Ниже предложены пути его улучшения. Выберите то, что интересно вам и действуйте.

1. Реализуйте автоматическое обновление данных (без кнопки Refresh).
<br>*Подсказка:* используйте класс **ThreadPoolTimer**.

2. Реализуйте Data Binding для автоматического обмена данными между элементами интерфейса и таблицей.

3. Добавьте элемент, в котором будет указано, сколько секунд назад был обмен данными с таблицей со стороны Tessel.
<br>*Подсказка:* в таблице есть автоматически создаваемое поле **__updatedAt** типа **date**, которое поможет вам определить момент последнего обновления записи таблицы.

4. Реализуйте аутентификацию пользователей и подумайте над привязкой устройства к пользователю.
<br>*Подсказки:*
 - Используйте аутентификацию при помощи учетной записи Microsoft, если у вас есть учетная запись разработчика Windows Store.
 - В таблице уже зарезервирован столбец **userid**, который позволит вам хранить идентификатор пользователя. Сделав выборку по идентификатору, из столбца **deviceid** можно будет получить все устройства, привязанные к данном пользователю, а затем получить данные от них.
 - Возможно, в приложении вам понадобится реализовать интерфейс для привязки нового устройства к данному пользователю. Вам придется изменить интерфейс приложения, если вы хотите работать с несколькими устройствами одновременно, так как данный учебный пример был приведен только для одного устройства.
 - При реализации аутентификации вам придется отредактировать скрипты (не путать с API) добавления данных в таблицу с целью проверки подлинности пользователя. Подробности вы можете получить, перейдя по ссылке [**Добавление проверки подлинности**](http://azure.microsoft.com/en-us/documentation/articles/mobile-services-javascript-backend-windows-universal-dotnet-get-started-users/), которая находится на стартовом экране мобильной службы Microsoft Azure.
 - Вы также можете (и должны) установить более жесткие ограничения на доступ к таблице (**Только прошедшие проверку пользователи** для всех операций).

5. Реализуйте универсальное приложение (Universal App), которое сможет также работать на устройствах под управлением Windows 8.1.

6. Подумайте, как можно реализовать Push-уведомления. В чем их польза для подобных решений?

7. Реализуйте возможность автоматического подстраивания интерфейса приложения под различные размеры экранов.

8. Добавьте обработку исключений при работе с таблицей данных.
